package com.ruoyi.library.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.constant.TwinConstants;
import com.ruoyi.common.core.utils.BeanCopyUtils;
import com.ruoyi.common.core.utils.DateUtils;
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.library.api.params.DigitalTwinParams;
import com.ruoyi.library.api.params.TwinRelationInfo.TwinRelationInfoParams;
import com.ruoyi.library.api.query.TwinRelationInfo.TwinRelationInfoQuery;
import com.ruoyi.library.api.vo.*;
import com.ruoyi.library.api.vo.TwinRelationInfo.TwinRelationInfoVO;
import com.ruoyi.library.domain.*;
import com.ruoyi.library.enums.TwinRelationType;
import com.ruoyi.library.mapper.DtsDigitalTwinMapper;
import com.ruoyi.library.service.*;
import com.ruoyi.library.api.query.DigitalTwinQuery;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 数字孪生体
 *
 * @author ZhangYu
 * @date 2024/05/21
 */
@Slf4j
@Service
public class DtsDigitalTwinServiceImpl extends ServiceImpl<DtsDigitalTwinMapper, DtsDigitalTwin> implements IDtsDigitalTwinService {

    @Resource
    private IDtsDigitalTwinRelationService digitalTwinRelationService;
    @Resource
    private RedisService redisService;

    /**
     * 过期时间（分钟）
     */
    private static final long EXPIRATION_TIME = 2 * 60;

    /**
     * 编码前缀
     */
    private static final String TWIN_CODE_PREFIX = "TWIN_CODE_";

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addDigitalTwin(DigitalTwinParams digitalTwinParams) {
        DtsDigitalTwin digitalTwin = BeanCopyUtils.copyNewBean(digitalTwinParams, DtsDigitalTwin.class);
        digitalTwin.setTwinCode(createTwinCode());
        baseMapper.insert(digitalTwin);
    }

    /**
     * 创建孪生编码 如：TWIN_CODE_00001
     *
     * @return 孪生编码
     */
    private synchronized String createTwinCode() {
        String Key = TwinConstants.TWIN_CODE_KEY;
        if (redisService.hasKey(Key)) {
            log.info("--------------------------------------------------------------------------------孪生编码存在redis中");
            Integer number = redisService.getCacheObject(Key);
            redisService.setCacheObject(Key, ++number, EXPIRATION_TIME, TimeUnit.MINUTES);
            return TWIN_CODE_PREFIX + String.format("%05d", number);
        }
        String maxTwinCode = baseMapper.getMaxTwinCode();
        if (StrUtil.isNotEmpty(maxTwinCode)) {
            log.info("--------------------------------------------------------------------------------孪生编码存在数据库中");
            String oldNumber = maxTwinCode.substring(maxTwinCode.length() - 5);
            int newNumber = Integer.parseInt(oldNumber);
            redisService.setCacheObject(Key, ++newNumber, EXPIRATION_TIME, TimeUnit.MINUTES);
            return TWIN_CODE_PREFIX + String.format("%05d", newNumber);
        }
        log.info("----------------------------------------------------------------------------孪生编码数据库、redis都不存在");
        redisService.setCacheObject(Key, 1, EXPIRATION_TIME, TimeUnit.MINUTES);
        return TWIN_CODE_PREFIX + String.format("%05d", 1);
    }

    @Override
    public DigitalTwinVO getTwinInfo(Long id) {
        return baseMapper.getTwinInfo(id);
    }

    @Override
    public Page<DigitalTwinVO> digitalTwinPage(DigitalTwinQuery digitalTwinQuery) {
        return baseMapper.digitalTwinPage(new Page<>(digitalTwinQuery.getPageNum(), digitalTwinQuery.getPageSize()), digitalTwinQuery);
    }

    @Override
    public Page<DigitalTwinVO> digitalTwinNoRelationPage(DigitalTwinQuery digitalTwinQuery) {
        return baseMapper.digitalTwinNoRelationPage(new Page<>(digitalTwinQuery.getPageNum(), digitalTwinQuery.getPageSize()), digitalTwinQuery);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addTwinRelationInfo(TwinRelationInfoParams twinRelationInfoParams) {
        List<DtsDigitalTwinRelation> digitalTwinRelationList = setDigitalTwinRelationList(twinRelationInfoParams);
        digitalTwinRelationService.saveBatch(digitalTwinRelationList);
    }

    /**
     * 设置关联关系
     *
     * @param twinRelationInfoParams 孪生体关联信息新增or修改参数
     * @return 关联关系列表
     */
    private List<DtsDigitalTwinRelation> setDigitalTwinRelationList(TwinRelationInfoParams twinRelationInfoParams) {
        List<DtsDigitalTwinRelation> digitalTwinRelationList = new ArrayList<>();
        addTwinRelationToList(digitalTwinRelationList, twinRelationInfoParams.getTwinId(), twinRelationInfoParams.getAssetsId(), TwinRelationType.ASSETS.getCode());
        addTwinRelationToList(digitalTwinRelationList, twinRelationInfoParams.getTwinId(), twinRelationInfoParams.getComponentId(), TwinRelationType.COMPONENT.getCode());
        addTwinRelationToList(digitalTwinRelationList, twinRelationInfoParams.getTwinId(), twinRelationInfoParams.getModeId(), TwinRelationType.MODE.getCode());
        addTwinRelationToList(digitalTwinRelationList, twinRelationInfoParams.getTwinId(), twinRelationInfoParams.getBusinessId(), TwinRelationType.BUSINESS.getCode());
        addTwinRelationToList(digitalTwinRelationList, twinRelationInfoParams.getTwinId(), twinRelationInfoParams.getSpaceId(), TwinRelationType.SPACE.getCode());
        return digitalTwinRelationList;
    }

    /**
     * 添加孪生关联关系
     *
     * @param digitalTwinRelationList 关联关系列表
     * @param twinId                  孪生id
     * @param relationId              关联id
     * @param relationType            关联类型
     */
    private void addTwinRelationToList(List<DtsDigitalTwinRelation> digitalTwinRelationList, Long twinId, Long relationId, Integer relationType) {
        if (ObjectUtil.isEmpty(relationId)) {
            return;
        }
        digitalTwinRelationList.add(DtsDigitalTwinRelation.builder().twinId(twinId).relationId(relationId).relationType(relationType).build());
    }

    @Override
    public void deleteTwinRelationInfo(Long twinId) {
        digitalTwinRelationService.remove(new QueryWrapper<DtsDigitalTwinRelation>().lambda().eq(DtsDigitalTwinRelation::getTwinId, twinId));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTwinRelationInfo(TwinRelationInfoParams twinRelationInfoParams) {
        List<DtsDigitalTwinRelation> digitalTwinRelationList = setDigitalTwinRelationList(twinRelationInfoParams);
        deleteTwinRelationInfo(twinRelationInfoParams.getTwinId());
        digitalTwinRelationService.saveBatch(digitalTwinRelationList);
    }

    @Override
    public TwinRelationInfoVO getTwinRelationInfoClass(Long twinId) {
        return baseMapper.getTwinRelationInfoClass(twinId);
    }

    @Override
    public TwinRelationInfoVO getTwinRelationInfoField(Long twinId) {
        return baseMapper.getTwinRelationInfoField(twinId);
    }

    @Override
    public Page<TwinRelationInfoVO> twinRelationInfoPage(TwinRelationInfoQuery twinRelationInfoQuery) {
        return baseMapper.twinRelationInfoPage(new Page<>(twinRelationInfoQuery.getPageNum(), twinRelationInfoQuery.getPageSize()), twinRelationInfoQuery);
    }

    @Override
    public TwinNumberStatisticsVO twinNumberStatistics() {
        return baseMapper.twinNumberStatistics();
    }

    @Override
    public List<Integer> numberChangeTrend() {
        SimpleDateFormat toYearDateFormat = new SimpleDateFormat(DateUtils.YYYY);
        List<String> timeList = new ArrayList<>();
        for (int month = 1; month <= 12; month++) {
            //用年月组合（yyyy-MM）去统计全年12个月份的数据
            String time = toYearDateFormat.format(new Date()) + StrUtil.DASHED + String.format("%02d", month);
            timeList.add(time);
        }
        return baseMapper.numberChangeTrend(timeList);
    }

    @Override
    public List<TwinRelationInfoVO> getTwinRelationInfoList(Long twinId) {
        return baseMapper.getTwinRelationInfoList(twinId);
    }

}
